package discovery

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/didi/nightingale/src/models"
	"github.com/influxdata/telegraf"
)

type BaseDiscovery struct {
	name    string
	newRule func() TelegrafDisconvery
}

func NewBaseDiscovery(name string, newRule func() TelegrafDisconvery) *BaseDiscovery {
	return &BaseDiscovery{
		name:    name,
		newRule: newRule,
	}
}

type TelegrafDisconvery interface {
	TelegrafInput() (telegraf.Input, error)
}

func (d BaseDiscovery) Name() string { return d.name }

// used for ui
func (d BaseDiscovery) Get(id int64) (interface{}, error) {
	collect := &models.CollectRule{}
	has, err := models.DB["mon"].Where("id = ?", id).Get(collect)
	if !has {
		return nil, err
	}
	return collect, err
}

func (d BaseDiscovery) mustGetRule(id int64) (*models.CollectRule, error) {
	collect := &models.CollectRule{}
	has, err := models.DB["mon"].Where("id = ?", id).Get(collect)
	if err != nil {
		return nil, err
	}
	if !has {
		return nil, fmt.Errorf("unable to get the collectRule")
	}
	return collect, nil
}

func (d BaseDiscovery) Gets(nids []int64) (ret []interface{}, err error) {
	collects := []models.CollectRule{}
	if len(nids) == 0 {
		err = models.DB["mon"].Where("collect_type=?", d.name).Find(&collects)
	} else {
		err = models.DB["mon"].Where("collect_type=?", d.name).In("nid", nids).Find(&collects)
	}
	for _, c := range collects {
		ret = append(ret, c)
	}
	return ret, err
}

func (d BaseDiscovery) GetByNameAndNid(name string, nid int64) (interface{}, error) {
	collect := &models.CollectRule{}
	has, err := models.DB["mon"].Where("collect_type = ? and name = ? and nid = ?", d.name, name, nid).Get(collect)
	if !has {
		return nil, err
	}
	return collect, err
}

func (d BaseDiscovery) Create(data []byte, username string) error {
	collect := &models.CollectRule{CollectType: d.name}
	rule := d.newRule()

	if err := json.Unmarshal(data, collect); err != nil {
		return fmt.Errorf("unmarshal body %s err:%v", string(data), err)
	}

	if err := collect.Validate(rule); err != nil {
		return err
	}

	can, err := models.UsernameCandoNodeOp(username, "mon_collect_create", collect.Nid)
	if err != nil {
		return err
	}
	if !can {
		return fmt.Errorf("permission deny")
	}

	now := time.Now().Unix()
	collect.Creator = username
	collect.CreatedAt = now
	collect.Updater = username
	collect.UpdatedAt = now

	old, err := d.GetByNameAndNid(collect.Name, collect.Nid)
	if err != nil {
		return err
	}
	if old != nil {
		return fmt.Errorf("同节点下策略名称 %s 已存在", collect.Name)
	}

	if err := models.CreateCollect(d.name, username, collect, collect.DryRun); err != nil {
		return err
	}

	return nil
}

type DryRun struct {
	msg string
}

func (p DryRun) Error() string {
	return p.msg
}
